package de.zainodis.commons.utils;

import java.io.ByteArrayOutputStream;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Base64;

public class DeEncodingUtils {

   public static final int THUMB_QUALITY = 40;
   public static final int HIGH_QUALITY = 90;

   public static final String UTF_8_ENCODING = "UTF-8";

   /**
    * Generous estimation of the memory an image might use up. The image in
    * question has the assumed dimensions 1024 x 768 and a color depths of 24
    * bit.
    */
   public static final long DEFAULT_IMAGE_SIZE = getSizeInBytes(1024, 768, 24);

   /**
    * Converts the given bitmap to a byte array and returns the result.
    * 
    * @param bitmap
    *           the bitmap that should be converted.
    * @return the result of the conversion.
    */
   public static byte[] bitmapToByteArray(Bitmap bitmap) {
	 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
	 bitmap.compress(Bitmap.CompressFormat.PNG, HIGH_QUALITY, outputStream);
	 // write this data to a blob
	 return outputStream.toByteArray();
   }

   /**
    * Converts the given bitmap to a byte array and returns the result using the
    * given quality setting. Available quality settings are: @see
    * {@link DeEncodingUtils#THUMB_QUALITY} and @see
    * {@link DeEncodingUtils#HIGH_QUALITY}. The quality must be a value between
    * zero and 100.
    * 
    * @param bitmap
    *           the bitmap that should be converted.
    * @return the result of the conversion.
    */
   public static byte[] bitmapToByteArray(Bitmap bitmap, int quality) {
	 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
	 bitmap.compress(Bitmap.CompressFormat.PNG, quality, outputStream);
	 // write this data to a blob
	 return outputStream.toByteArray();
   }

   public static String byteArrayToBase64String(byte[] value) {
	 return Base64.encodeToString(value, Base64.DEFAULT);
   }

   /**
    * Converts the given bitmap to a string and returns the result. The bitmap
    * is first converted to a byte array and then encoded to a String using
    * {@link Base64} encoding.
    * 
    * @param bitmap
    *           the bitmap that should be converted.
    * @return a base64 encoded string.
    */
   public static String bitmapToString(Bitmap bitmap) {
	 byte[] bytes = bitmapToByteArray(bitmap);
	 return byteArrayToBase64String(bytes);
   }

   /**
    * Converts the given byte array to a bitmap and returns the result.
    */
   public static Bitmap byteArrayToBitmap(byte[] imageInBytes) {
	 return BitmapFactory.decodeByteArray(imageInBytes, 0, imageInBytes.length, getOptions());
   }

   /**
    * Reads the given resource (must be a drawable!) into a Bitmap.
    * 
    * @param resources
    *           the resources object containing the image data.
    * @param resourceID
    *           the reference id for the resource that should be converted to a
    *           bitmap.
    * @return a Bitmap that has been created from the given resource.
    */
   public static Bitmap resourceToBitmap(Resources resources, int resourceID) {
	 return BitmapFactory.decodeResource(resources, resourceID, getOptions());
   }

   /**
    * Takes a {@link Base64} encoded string, converts it to a byte array and
    * returns the result.
    */
   public static byte[] base64StringToByteArray(String value) {
	 return Base64.decode(value.getBytes(), Base64.DEFAULT);
   }

   /**
    * Takes a {@link Base64} encoded string, converts it to a byte array and
    * then to a {@link Bitmap} and returns the resulting bitmap.
    */
   public static Bitmap base64StringToBitmap(String value) {
	 byte[] bytes = base64StringToByteArray(value);
	 return byteArrayToBitmap(bytes);
   }

   /**
    * Scales the bitmap based on the given target width.
    * 
    * @param original
    *           the original bitmap to scale.
    * @param targetWidth
    *           the target width of the scaled bitmap.
    * @return the scaled bitmap.
    */
   public static Bitmap createScaledBitmap(Bitmap original, int targetWidth) {
	 float scaleFactor = original.getWidth() / (float) targetWidth;
	 int targetHeight = Math.round(original.getHeight() / scaleFactor);

	 return Bitmap.createScaledBitmap(original, targetWidth, targetHeight, true);
   }

   /**
    * Scales the bitmap based on the given target height.
    * 
    * @param original
    *           the original bitmap to scale.
    * @param targetWidth
    *           the target height of the scaled bitmap.
    * @return the scaled bitmap.
    */
   public static Bitmap createScaledBitmap(int targetHeight, Bitmap original) {
	 // Target height relates to original height like target width relates to
	 // original width, the unknown is the target width
	 float scaleFactor = original.getHeight() / (float) targetHeight;
	 int targetWidth = Math.round(original.getWidth() / scaleFactor);

	 return Bitmap.createScaledBitmap(original, targetWidth, targetHeight, true);
   }

   /**
    * @return a set of performance-optimised options.
    */
   private static BitmapFactory.Options getOptions() {
	 BitmapFactory.Options options = new BitmapFactory.Options();
	 options.inPurgeable = true;
	 options.inInputShareable = true;
	 return options;
   }

   /**
    * @param bitmap
    *           the bitmap whose size should be calculated.
    * @return fairly accurate size of the given {@link Bitmap}; or zero if the
    *         given bitmap is null.
    */
   public static long getSizeInBytes(Bitmap bitmap) {
	 if (bitmap == null) {
	    return 0;
	 }

	 return bitmap.getRowBytes() * bitmap.getHeight();
   }

   /**
    * 
    * @param width
    *           the width of the image.
    * @param height
    *           the height of the image.
    * @param colorDepthInBits
    *           the color depths of the image.
    * @return the calculated height of an image with the given dimensions - this
    *         might not be 100% accurate and should be understood as an
    *         estimate.
    */
   public static long getSizeInBytes(long width, long height, long colorDepthInBits) {
	 return Math.round(width * height * colorDepthInBits / 8);
   }

}
